package com.gyj.server.core;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.concurrent.atomic.AtomicLong;

/**
 * 心跳计数器
 * @author Gao
 * @date 2021/6/26
 */
public class HeartBeatCounter {

    private static final Logger logger = LogManager.getLogger(HeartBeatCounter.class);

    /**
     * 时间间隔
     */
    private static final long TIME_INTERVAL = 60 * 1000L;

    private static HeartBeatCounter instance = new HeartBeatCounter();

    /**
     * 初始化计数器组件的时候
     * 启动一个每分钟心跳次数清零的线程
     */
    private HeartBeatCounter() {
        Daemon daemon = new Daemon();
        daemon.setDaemon(true);
        daemon.start();
    }

    /**
     * 单例模式的心跳计数器
     * @return
     */
    public static HeartBeatCounter getInstance() {
        return instance;
    }

    //private long latestHeartbeatCount = 0L;
    //private AtomicLong latestHeartbeatCount = new AtomicLong(0L);
    /**
     * 最近一分钟的心跳次数
     * java8分段加锁累加的机制
     */
    ///private LongAdder latestHeartbeatCount = new LongAdder();
    /**
     * AtomicLong
     */
    private AtomicLong latestHeartbeatCount = new AtomicLong(0L);

    /**
     * 最近一次心跳的时间
     */
    private volatile long latestHeartbeatTime = System.currentTimeMillis();

    /**
     * 每分钟心跳次数清零的线程
     */
    private class Daemon extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    /*synchronized (HeartBeatCounter.class) {
                        logger.info("每分钟心跳次数清零的线程, 上一次发心跳时间：{}", latestHeartbeatTime);
                        if (System.currentTimeMillis() - latestHeartbeatTime > TIME_INTERVAL) {
                            latestHeartbeatTime = System.currentTimeMillis();
                            ///latestHeartbeatCount = new AtomicLong(0L);
                            ///latestHeartbeatCount = new LongAdder();

                            logger.info("每分钟心跳次数清零的线程, 心跳次数清0");
                        }
                    }*/
                    // 优化锁，使用CAS无锁机制
                    if (System.currentTimeMillis() - latestHeartbeatTime > TIME_INTERVAL) {
                        // CAS操作
                        while (true) {
                            long expectedValue = latestHeartbeatCount.get();
                            if (latestHeartbeatCount.compareAndSet(expectedValue, 0L)) {
                                logger.info("每分钟心跳次数清零的线程，使用CAS无锁机制设置为0");
                                break;
                            }
                        }
                        latestHeartbeatTime = System.currentTimeMillis();
                    }
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    logger.error("每分钟心跳次数清零的线程,异常e:{}", e);
                }
            }
        }
    }

    /**
     * 计数器递增
     * synchronized (HeartBeatCounter.class) {
     *   latestHeartbeatCount++;
     * }
     */
    public void increment() {
        // 这个synchronized上锁，性能其实是很差的
        // 因为可能会有很多的线程，不断的接受到心跳的请求，就会来增加心跳次数
        // 多线程卡在这里，一个一个排队
        // 依次上锁，累加1，再次释放锁，会有一个问题

        // 如果你的服务实例很多的话，1万个服务实例，每秒可能都有很多个请求过来更新心跳
        // 如果在这里加了synchronized的话，会影响并发的性能
        // 换成了AtomicLong原子类之后，不加锁，无锁化，CAS操作，保证原子性，还可以多线程并发
        ///latestHeartbeatCount.increment();
        latestHeartbeatCount.incrementAndGet();
    }

    /**
     * 获取最近一分钟的心跳次数
     * @return
     */
    public long getCount() {
        ///return latestHeartbeatCount.intValue();
        return latestHeartbeatCount.get();
    }
}
